#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <uv.h>

#define DEFAULT_PORT 7000
#define DEFAULT_BACKLOG 128

struct server_context {
    uv_loop_t *loop;
    uv_tcp_t server;
};

static void server_context_init(struct server_context *ctx, uv_loop_t *loop) {
    ctx->loop = loop;
    uv_tcp_init(ctx->loop, &ctx->server);
    ctx->server.data = ctx;
}

static void server_context_cleanup(struct server_context *ctx) {
    uv_close((uv_handle_t *)&ctx->server, NULL);
}

static void alloc_cb(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
    (void)handle;
    buf->base = malloc(suggested_size);
    buf->len = suggested_size;
}

static void close_cb(uv_handle_t *handle) {
    fprintf(stdout, "Client connection closed\n");
    free(handle);
}

static void write_cb(uv_write_t *req, int status);
static void read_cb(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) {
    if (nread <= 0) {
        if (nread < 0 &&nread != UV_EOF) {
            fprintf(stderr, "Server read error: %s\n", uv_strerror(nread));
        } else {
            fprintf(stdout, "Connection closed\n");
        }

        if (buf->base) {
            free(buf->base);
        }
        uv_close((uv_handle_t *)client, close_cb);
    } else if (nread > 0) {
        fprintf(stdout, "Server received: '%.*s'\n", (int)nread, buf->base);

        uv_write_t *req = malloc(sizeof(uv_write_t));
        req->bufs = malloc(sizeof(uv_buf_t));
        char *data = malloc(nread);
        memcpy(data, buf->base, nread);
        req->bufs[0] = uv_buf_init(data, nread);
        uv_write(req, (uv_stream_t *)client, req->bufs, 1, write_cb);
        if (buf->base) {
            free(buf->base);
        }
    }
}

static void write_cb(uv_write_t *req, int status) {
    if (status < 0) {
        fprintf(stderr, "Server write error: %s\n", uv_strerror(status));
    } else {
        fprintf(stdout, "Server write success\n");
    }
    if (req->bufs) {
        if (req->bufs[0].base) {
            free(req->bufs[0].base);
        }

        free(req->bufs);
    }
    if (req) {
        free(req);
    }
}

static void on_new_connection(uv_stream_t *server, int status) {
    if (status < 0) {
        fprintf(stderr, "New connection error: %s\n", uv_strerror(status));
        return;
    }

    uv_tcp_t *client = malloc(sizeof(uv_tcp_t));
    uv_tcp_init(server->loop, client);
    if (uv_accept(server, (uv_stream_t *)client) < 0) {
        free(client);
        return;
    }
    struct sockaddr_in addr;
    int len = sizeof(addr);
    uv_tcp_getsockname((uv_tcp_t *)client, (struct sockaddr *)&addr, &len);
    fprintf(stdout, "New connection from %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

    fprintf(stdout, "Server start reading\n");
    uv_read_start((uv_stream_t *)client, alloc_cb, read_cb);
}

static void signal_cb(uv_signal_t *handle, int signum) {
    fprintf(stdout, "Signal %d received\n", signum);
    struct server_context *ctx = (struct server_context *)handle->data;
    uv_stop(ctx->loop);
}

int main() {
    uv_loop_t *loop = uv_default_loop();
    struct server_context ctx;
    server_context_init(&ctx, loop);

    uv_signal_t signal;
    uv_signal_init(loop, &signal);
    signal.data = &ctx;
    uv_signal_start(&signal, signal_cb, SIGINT);

    struct sockaddr_in addr;
    uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);

    uv_tcp_bind(&ctx.server, (const struct sockaddr *)&addr, 0);
    fprintf(stdout, "Server is running on port %d\n", DEFAULT_PORT);
    uv_listen((uv_stream_t *)&ctx.server, DEFAULT_BACKLOG, on_new_connection);

    int ret = uv_run(loop, UV_RUN_DEFAULT);

    server_context_cleanup(&ctx);
    uv_loop_close(loop);

    return ret;
}